package com.ruoyi.common.utils.jwt;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.Map;
import java.util.UUID;
import com.ruoyi.common.utils.DateUtils;

/**
 * JWT工具类
 * 
 * @author ruoyi
 */
public class JwtUtils
{
    /**
     * Token黑名单检查接口
     */
    public interface TokenBlacklistChecker {
        boolean isTokenBlacklisted(String tokenId);
    }

    /**
     * Token验证结果类
     */
    public static class TokenValidationResult {
        private boolean valid;
        private String tokenInfo;
        private String tokenId;
        private Long userId;
        private String username;
        private String tokenType;
        private Date expiration;
        
        public TokenValidationResult(boolean valid, String tokenInfo) {
            this.valid = valid;
            this.tokenInfo = tokenInfo;
        }
        
        public TokenValidationResult(boolean valid, String tokenInfo, String tokenId, Long userId, String username, String tokenType, Date expiration) {
            this.valid = valid;
            this.tokenInfo = tokenInfo;
            this.tokenId = tokenId;
            this.userId = userId;
            this.username = username;
            this.tokenType = tokenType;
            this.expiration = expiration;
        }
        
        // Getters
        public boolean isValid() { return valid; }
        public String getTokenInfo() { return tokenInfo; }
        public String getTokenId() { return tokenId; }
        public Long getUserId() { return userId; }
        public String getUsername() { return username; }
        public String getTokenType() { return tokenType; }
        public Date getExpiration() { return expiration; }
    }
    private static final Logger log = LoggerFactory.getLogger(JwtUtils.class);

    /**
     * 默认密钥
     */
    private static final String DEFAULT_SECRET = "ruoyi-jwt-secret-key-for-token-generation-and-validation";

    /**
     * 默认过期时间（2小时）
     */
    private static final long DEFAULT_EXPIRE_TIME = 2 * 60 * 60 * 1000L;

    /**
     * 默认刷新Token过期时间（7天）
     */
    private static final long DEFAULT_REFRESH_EXPIRE_TIME = 7 * 24 * 60 * 60 * 1000L;

    /**
     * 生成密钥
     */
    private static SecretKey getSecretKey(String secret)
    {
        if (!StringUtils.hasText(secret))
        {
            secret = DEFAULT_SECRET;
        }
        return Keys.hmacShaKeyFor(secret.getBytes());
    }

    /**
     * 生成访问Token
     * 
     * @param userId 用户ID
     * @param username 用户名
     * @param secret 密钥
     * @param expireTime 过期时间（毫秒）
     * @return Token字符串
     */
    public static String createAccessToken(Long userId, String username, String secret, Long expireTime)
    {
        return createAccessToken(UUID.randomUUID().toString(), userId, username, secret, expireTime);
    }
    
    /**
     * 生成访问Token（指定Token ID）
     * 
     * @param tokenId Token ID
     * @param userId 用户ID
     * @param username 用户名
     * @param secret 密钥
     * @param expireTime 过期时间（毫秒）
     * @return Token字符串
     */
    public static String createAccessToken(String tokenId, Long userId, String username, String secret, Long expireTime)
    {
        if (expireTime == null)
        {
            expireTime = DEFAULT_EXPIRE_TIME;
        }
        
        Date now = new Date();
        Date expiration = new Date(now.getTime() + expireTime);
        
        return Jwts.builder()
                .setId(tokenId)
                .setSubject(username)
                .claim("userId", userId)
                .claim("username", username)
                .claim("tokenType", "access")
                .setIssuedAt(now)
                .setExpiration(expiration)
                .signWith(getSecretKey(secret))
                .compact();
    }

    /**
     * 生成刷新Token
     * 
     * @param userId 用户ID
     * @param username 用户名
     * @param secret 密钥
     * @return 刷新Token字符串
     */
    public static String createRefreshToken(Long userId, String username, String secret)
    {
        return createRefreshToken(userId, username, secret, null);
    }

    /**
     * 生成刷新Token
     * 
     * @param userId 用户ID
     * @param username 用户名
     * @param secret 密钥
     * @param expireTime 过期时间（毫秒）
     * @return 刷新Token字符串
     */
    public static String createRefreshToken(Long userId, String username, String secret, Long expireTime)
    {
        return createRefreshToken(UUID.randomUUID().toString(), userId, username, secret, expireTime);
    }
    
    /**
     * 生成刷新Token（指定Token ID）
     * 
     * @param tokenId Token ID
     * @param userId 用户ID
     * @param username 用户名
     * @param secret 密钥
     * @param expireTime 过期时间（毫秒）
     * @return 刷新Token字符串
     */
    public static String createRefreshToken(String tokenId, Long userId, String username, String secret, Long expireTime)
    {
        if (expireTime == null)
        {
            expireTime = DEFAULT_REFRESH_EXPIRE_TIME;
        }
        
        Date now = new Date();
        Date expiration = new Date(now.getTime() + expireTime);
        
        return Jwts.builder()
                .setId(tokenId)
                .setSubject(username)
                .claim("userId", userId)
                .claim("username", username)
                .claim("tokenType", "refresh")
                .setIssuedAt(now)
                .setExpiration(expiration)
                .signWith(getSecretKey(secret))
                .compact();
    }

    /**
     * 解析Token
     * 
     * @param token Token字符串
     * @param secret 密钥
     * @return Claims
     */
    public static Claims parseToken(String token, String secret)
    {
        try
        {
            return Jwts.parserBuilder()
                    .setSigningKey(getSecretKey(secret))
                    .build()
                    .parseClaimsJws(token)
                    .getBody();
        }
        catch (Exception e)
        {
            log.error("解析Token失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 验证Token是否有效
     * 
     * @param token Token字符串
     * @param secret 密钥
     * @return 是否有效
     */
    public static boolean validateToken(String token, String secret)
    {
        try
        {
            Claims claims = parseToken(token, secret);
            return claims != null && !isTokenExpired(claims);
        }
        catch (Exception e)
        {
            log.error("验证Token失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 检查Token是否过期
     * 
     * @param claims Token声明
     * @return 是否过期
     */
    public static boolean isTokenExpired(Claims claims)
    {
        return claims.getExpiration().before(new Date());
    }

    /**
     * 从Token中获取用户ID
     * 
     * @param token Token字符串
     * @param secret 密钥
     * @return 用户ID
     */
    public static Long getUserId(String token, String secret)
    {
        Claims claims = parseToken(token, secret);
        if (claims != null)
        {
            Object userId = claims.get("userId");
            if (userId instanceof Integer)
            {
                return ((Integer) userId).longValue();
            }
            else if (userId instanceof Long)
            {
                return (Long) userId;
            }
        }
        return null;
    }

    /**
     * 从Token中获取用户名
     * 
     * @param token Token字符串
     * @param secret 密钥
     * @return 用户名
     */
    public static String getUsername(String token, String secret)
    {
        Claims claims = parseToken(token, secret);
        return claims != null ? claims.getSubject() : null;
    }

    /**
     * 从Token中获取Token ID
     * 
     * @param token Token字符串
     * @param secret 密钥
     * @return Token ID
     */
    public static String getTokenId(String token, String secret)
    {
        Claims claims = parseToken(token, secret);
        return claims != null ? claims.getId() : null;
    }

    /**
     * 从Token中获取Token类型
     * 
     * @param token Token字符串
     * @param secret 密钥
     * @return Token类型
     */
    public static String getTokenType(String token, String secret)
    {
        Claims claims = parseToken(token, secret);
        return claims != null ? (String) claims.get("tokenType") : null;
    }

    /**
     * 获取Token的过期时间
     * 
     * @param token Token字符串
     * @param secret 密钥
     * @return 过期时间
     */
    public static Date getExpiration(String token, String secret)
    {
        Claims claims = parseToken(token, secret);
        return claims != null ? claims.getExpiration() : null;
    }

    /**
     * 获取Token的剩余有效时间（毫秒）
     * 
     * @param token Token字符串
     * @param secret 密钥
     * @return 剩余有效时间
     */
    public static long getRemainingTime(String token, String secret)
    {
        Date expiration = getExpiration(token, secret);
        if (expiration != null)
        {
            return expiration.getTime() - System.currentTimeMillis();
        }
        return 0;
    }

    /**
     * 生成Token ID
     * 
     * @return Token ID
     */
    public static String generateTokenId()
    {
        return UUID.randomUUID().toString().replace("-", "");
    }

    /**
     * 完整的Token验证（包含数据库状态检查）
     * 
     * @param token Token字符串
     * @param secret 密钥
     * @param blacklistChecker 黑名单检查器
     * @return Token验证结果
     */
    public static TokenValidationResult validateTokenWithDetails(String token, String secret, TokenBlacklistChecker blacklistChecker)
    {
        try
        {
            // 使用JwtUtils解析Token
            if (validateToken(token, secret))
            {
                // 获取Token ID用于数据库状态检查
                String tokenId = getTokenId(token, secret);
                
                // 检查Token在数据库中的状态
                if (blacklistChecker == null || !blacklistChecker.isTokenBlacklisted(tokenId))
                {
                    Long userId = getUserId(token, secret);
                    String username = getUsername(token, secret);
                    String tokenType = getTokenType(token, secret);
                    Date expiration = getExpiration(token, secret);
                    
                    String tokenInfo = String.format(" [Token信息: 用户ID=%s, 用户名=%s, Token类型=%s, 过期时间=%s, 数据库状态=正常]", 
                        userId, username, tokenType, 
                        expiration != null ? DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, expiration) : "未知");
                    
                    return new TokenValidationResult(true, tokenInfo, tokenId, userId, username, tokenType, expiration);
                }
                else
                {
                    return new TokenValidationResult(false, " [Token已被撤销或停用]");
                }
            }
            else
            {
                return new TokenValidationResult(false, " [Token无效或已过期]");
            }
        }
        catch (Exception e)
        {
            return new TokenValidationResult(false, " [Token解析失败: " + e.getMessage() + "]");
        }
    }
}